package com.yesep.learn.design.structural.composite;

import com.yesep.learn.design.common.pojo.*;
import com.yesep.learn.design.common.util.*;

/**
 * 组合模式（Composite Pattern）是一种结构型设计模式，它允许将对象组合成树形结构以表示部分-整体的层次关系。
 * 通过组合模式，用户可以像处理单个对象一样处理组合对象和叶子对象，从而简化了客户端代码并提高了系统的灵活性。
 * 以下是关于组合模式的详细解析：
 * <p>
 * 一、定义与原理
 * 组合模式的核心思想是将对象组合成树形结构，以表示部分与整体的层次关系。
 * 在组合模式中，每个节点都可以是一个叶子节点（代表单个对象）或一个组合节点（代表包含其他节点的容器）。
 * 叶子节点和组合节点都实现了相同的接口，这样客户端就可以对它们进行一致的操作，而无需关心它们的具体类型。
 * <p>
 * 二、核心组件
 * 组件接口（Component）：为组合中的对象声明一个接口，在适当的情况下，声明接口方法实现缺省行为，声明一个用于访问组件子对象的接口。
 * 叶子节点（Leaf）：叶子节点是组合中的基本对象，叶子节点没有子节点。
 * 组合节点（Composite）：组合节点包含子节点，组合节点实现组件接口并包含子组件的集合，提供管理子组件的方法，如添加、删除等，并可以在适当的情况下重写父类的方法以实现递归行为。
 * 三、优点
 * 简化客户端代码：客户端可以一致地处理单个对象和组合对象，无需区分它们的类型。
 * 提高系统的灵活性：可以轻松地添加新的组合节点或叶子节点，而无需修改现有代码，满足开闭原则。
 * 更好的层次结构：清晰地定义了部分与整体的层次关系，便于理解和维护。
 * 四、缺点
 * 设计较复杂：客户端需要花费更多时间来理清类之间的层次关系。
 * 不容易限制容器中的构件：组合对象可以包含任意类型的组件，这可能导致一些操作在特定组合中无法有效执行。
 * 性能问题：在处理层次结构较深、组件较多的情况下，递归操作可能会导致性能问题。
 * 五、应用场景
 * 组合模式适用于需要表示对象整体与部分层次结构的场景，如：
 * <p>
 * 文件系统和目录结构：文件夹可以包含文件和其他文件夹，形成一个树形结构。通过组合模式，可以一致地处理文件和文件夹。
 * 组织架构和人员管理：部门可以包含员工和其他部门，形成一个层次结构。通过组合模式，可以一致地管理单个员工和组合部门。
 * 菜单和菜单项：菜单系统通常包含菜单项和子菜单，可以使用组合模式来构建菜单层次结构。
 * 图形编辑器和UI控件：设计各种形状和UI控件，如窗口、面板等，这些控件可以包含其他控件形成复杂的层次结构。
 * 六、示例
 * 以文件系统为例，可以定义一个抽象的文件组件接口，然后分别实现文件和文件夹类。文件和文件夹都实现了文件组件接口，因此可以统一处理它们。
 * 例如，可以编写一个方法来遍历并打印文件系统的结构，无论遇到的是文件还是文件夹，都可以使用相同的方法来处理。
 * <p>
 * 综上所述，组合模式是一种非常有用的设计模式，它可以帮助我们更好地表示和处理对象之间的层次关系，提高系统的灵活性和可维护性。
 * 然而，在使用时也需要注意其可能带来的设计复杂性和性能问题。
 */
public class Main {
    public static void main(String[] args) {
        Computer hp = DemoUtil.createHPComputer();
        hp.addComponent(DemoUtil.createCPUComponent());
        hp.addComponent(DemoUtil.createRAMComponent());
        hp.addComponent(DemoUtil.createStorageComponent());
        hp.getComponents().forEach(System.out::println);
        System.out.println("电脑功耗=" + hp.calculateConsumption());

        fileSystem();
    }

    public static void fileSystem() {
        IFileSystemComponent root = new Directory("root");
        IFileSystemComponent documents = new Directory("documents");
        IFileSystemComponent pictures = new Directory("pictures");

        IFileSystemComponent doc1 = new File("report.doc");
        IFileSystemComponent doc2 = new File("memo.txt");
        IFileSystemComponent pic1 = new File("pic1.jpg");
        IFileSystemComponent pic2 = new File("pic2.png");

        documents.add(doc1);
        documents.add(doc2);
        pictures.add(pic1);
        pictures.add(pic2);

        root.add(documents);
        root.add(pictures);

        root.display(0);
    }
}
